import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Random;

/**
 * 算式持久化
 *
 * @author Li Sipeng
 * @version 4.0 on   2022-11-12 21:12
 */
public class BinaryOPeration_3 {
    public static void main(String[] args) throws IOException {
        final int EQUATION_NUM = 50;   // 习题中等式的数量
        final int ADD_MAX_VALUE = 100; // 加法算式约束：加法结果的最大值   .
        final int MAX_OPRT_VALUE = 99;  // 算式约束：操作数的最大值
        final int SUB_MIN_VALUE = 0;    //减法算式约束：减法结果的最小值
        final int NUM_PER_LINE = 5;    //打印格式：每行算式个数
        int[][] exercise;              // 习题集二维数组
        final int modelGenerate = 0;   //产生算式模式：0批量产生并存储，1从文件中批量读取

        if(modelGenerate ==0) {  //批量生成指定数量加法、减法、混合算式题并存储到文件
            /* 打印加法算式习题  */
            printHeader("--程序输出加法算式的习题--");  // 打印输出信息头
            exercise = generateExerciseOfAdditionEquations(EQUATION_NUM,
                    ADD_MAX_VALUE, MAX_OPRT_VALUE);  // 产生加法算式习题
            formatAndDisplayExercise(exercise, NUM_PER_LINE, 0); //打印习题

            /* 打印减法算式习题  */
            printHeader("--程序输出减法算式的习题--");  // 打印输出信息头
            exercise = generateExerciseOfSubtractionEquations(EQUATION_NUM,
                    SUB_MIN_VALUE, MAX_OPRT_VALUE);  // 产生加法算式习题
            formatAndDisplayExercise(exercise, NUM_PER_LINE, 1); //打印习题

            /* 打印加减法混合算式习题  */
            printHeader("--程序输出加减法混合算式的习题--");  // 打印输出信息头
            exercise = generateExerciseOfAddAndSubEquations(EQUATION_NUM,
                    ADD_MAX_VALUE, SUB_MIN_VALUE, MAX_OPRT_VALUE);  // 产生加法算式习题
            formatAndDisplayExercise(exercise, NUM_PER_LINE, 2); //打印习题
        }else{
            getExerciseOfAdditionEquations(EQUATION_NUM);  //从文件中读取批量算式


        }
    }

    /*
    * 批量读取指定个数和类型的算式
    * @param equ_num：习题集中算式的数量
    * */
    public static int[][] getExerciseOfAdditionEquations(int equ_num) throws IOException {
        int[][] exercise = new int[equ_num][4];
        FileReader fr = new FileReader("AddOperation.txt");
        int str;
        while ((str=fr.read()) != -1){
            System.out.println(str);
        }
        fr.close();
        return exercise;
    }

    /**
     * 产生加法算式习题集
     * @param equ_num：习题集中算式的数量
     * @param add_max_value：加法结果的最大值
     * @param max_oprt_value：操作数的最大值
     * @return：返回习题二维数组[equation_num][4]
     */
    public static int[][] generateExerciseOfAdditionEquations(int equ_num,
                                                              int add_max_value, int max_oprt_value){
        int m, n;  //操作数
        int e[];   // 算式     //[操作数1，操作数2，操作符，结果]
        int[][] exercise = new int[equ_num][4];
        int e_index = 0; //目前习题中算式的个数

        for(int i = 0; i < equ_num; i++) {
            do {
                e = generateAnAddEquation(add_max_value, max_oprt_value); //产生一个加法算式
            }while(occursIn(e, exercise, e_index));  //判断算式e是否出现在习题exercise中
            exercise[e_index++] = e;   // 把算式e存放在习题exercise中，游标加1
        }

        return exercise;
    }

    /**
     * 产生减法算式习题集
     * @param equ_num：习题集中算式的数量
     * @param sub_min_value：减法结果的最小值
     * @param max_oprt_value：操作数的最大值
     * @return:返回习题二维数组[equation_num][4]
     */
    public static int[][] generateExerciseOfSubtractionEquations( int equ_num,
                                                                  int sub_min_value, int max_oprt_value){
        int m, n;  //操作数
        int e[];   // 算式     //[操作数1，操作数2，操作符，结果]
        int[][] exercise = new int[equ_num][4];
        int e_index = 0; //目前习题中算式的个数

        for(int i = 0; i < equ_num; i++) {
            do {
                e = generateAnSubtractEquation(sub_min_value, max_oprt_value); //产生一个减法算式
            }while(occursIn(e, exercise, e_index));  //判断算式e是否出现在习题exercise中
            exercise[e_index++] = e;   // 把算式e存放在习题exercise中，游标加1
        }

        return exercise;
    }

    /**
     * 产生加减法算式习题集
     * @param equ_num：习题集中算式的数量
     * @param add_max_value：加法结果的最大值
     * @param sub_min_value：减法结果的最小值
     * @param max_oprt_value：操作数的最大值
     * @return：返回习题二维数组[equation_num][4]
     */
    public static int[][] generateExerciseOfAddAndSubEquations(int equ_num,
                                                               int add_max_value, int sub_min_value, int max_oprt_value){
        int m, n;  //操作数
        int e[];   // 算式     //[操作数1，操作数2，操作符，结果]
        int[][] exercise = new int[equ_num][4];
        int e_index = 0; //目前习题中算式的个数
        int operator; //操作符（随机产生）
        Random random = new Random();

        for(int i = 0; i < equ_num; i++) {
            operator = random.nextInt(2); //0---加法；1---减法
            do {
                if(operator == 0) { //加法
                    e = generateAnAddEquation(add_max_value, max_oprt_value); //产生一个加法算式
                }else {  //减法
                    e = generateAnSubtractEquation(sub_min_value, max_oprt_value); //产生一个减法算式
                }

            }while(occursIn(e, exercise, e_index));  //判断算式e是否出现在习题exercise中
            exercise[e_index++] = e;   // 把算式e存放在习题exercise中，游标加1
        }

        return exercise;
    }

    /**
     * 产生一个加法算式
     * @param add_max_value：加法结果的最大值
     * @param max_oprt_value： 操作数的最大值
     * @return： 返回一个数组表示的算式[操作数1，操作数2，操作符，结果]，操作符：0--加法；1--减法
     */
    public static int[] generateAnAddEquation(int add_max_value, int max_oprt_value) {

        int m, n, v;
        int e[] = new int[] {0,0,0,0};
        do {
            m = generateOperand(max_oprt_value);  // 产生一个不超过max_oprt_value的操作数
            n = generateOperand(max_oprt_value);  // 产生一个不超过max_oprt_value的操作数
            v = m + n;
        }while(v > add_max_value);
        e[0] = m; //第一个操作数
        e[1] = n; //第二个操作数
        e[2] = 0; //操作符：0--加法；1--减法
        e[3] = v; // 计算结果
        return e;
    }

    /**
     * 产生一个减法算式
     * @param sub_min_value：减法结果的最小值
     * @param max_oprt_value： 操作数的最大值
     * @return： 返回一个数组表示的算式[操作数1，操作数2，操作符，结果]，操作符：0--加法；1--减法
     */
    public static int[] generateAnSubtractEquation(int sub_min_value, int max_oprt_value) {

        int m, n, v;
        int e[] = new int[] {0,0,0,0};
        do {
            m = generateOperand(max_oprt_value);  // 产生一个不超过max_oprt_value的操作数
            n = generateOperand(max_oprt_value);  // 产生一个不超过max_oprt_value的操作数
            v = m - n;
        }while(v < sub_min_value);
        e[0] = m; //第一个操作数
        e[1] = n; //第二个操作数
        e[2] = 1; //操作符：0--加法；1--减法
        e[3] = v; // 计算结果
        return e;
    }

    /**
     * 产生一个不超过 max_value的随机数
     * @param max_value
     * @return
     */
    public static int generateOperand(int max_value) {
        Random random = new Random();
        return random.nextInt(max_value+1);
    }

    /**
     * 判断算式在习题中是否重复
     * @param eq： 算式[操作数1，操作数2，操作符，结果]
     * @param exercise：习题集二维数组
     * @param index：目前习题中算式的个数（游标）
     * @return：返回布尔值，重复返回1；不重复返回0.
     */
    public static boolean occursIn(int[] eq, int[][] exercise, int index) {
        boolean found = false;
        for(int i = 0; i < index; i++) {
            if(isEqual(eq, exercise[i])) {
                found = true;
                break;
            }
        }
        return found;
    }

    /**
     * 判断两个算式是否相等
     * @param eq1：第一个算式  [操作数1，操作数2，操作符，结果]
     * @param eq2：第二个算式  [操作数1，操作数2，操作符，结果]
     * @return：如果相等，返回1；如果不相等，返回0
     */
    public static boolean isEqual(int[] eq1, int[] eq2) {
        return (eq1[0] == eq2[0] && eq1[1] == eq2[1] && eq1[2] == eq2[2])
                || (eq1[0] == eq2[1] && eq1[1] == eq2[0] && eq1[2] == eq2[2]);
    }

    /**
     * 按格式打印习题集
     * @param exercise： 习题集二维数组[equation_num][4]
     * @param num_per_line： 每行显示的算式个数
     * @param OperationFileNo： 生成的算式类型 0加、1减、2混合
     */
    public static void formatAndDisplayExercise(int[][] exercise, int num_per_line,int OperationFileNo) throws IOException {
        int i, m, n, o, v;
        int e[];  //算式数组[操作数1，操作数2，操作符，结果]

        FileWriter fw;
        if (OperationFileNo == 0){
            fw = new FileWriter("AddOperation.txt");
        }else if (OperationFileNo ==1 ){
            fw = new FileWriter("SubOperation.txt");
        }else{
            fw = new FileWriter("AddSubOperation.txt");
        }
        for(i = 0; i < exercise.length; i++) {
            e = exercise[i];
            m = e[0];
            n = e[1];
            o = e[2];
            v = e[3];
            if(i % num_per_line == 0) {
                System.out.println();
                System.out.printf("%2d ~ %2d:    ", i+1, i+5);
            }
            if(o == 0) {  //加法算式,操作符：0--加法；1--减法
                System.out.printf("%2d + %2d = %3d    ", m, n, v);
                fw.write(m+"+"+n+"="+v+"\n");
            }else {
                System.out.printf("%2d - %2d = %3d    ", m, n, v);
                fw.write(m+"-"+n+"="+v+"\n");
            }
        }
        fw.close();
    }

    /**
     * 打印输出提示信息
     *
     * @param str: 要打印的提示信息
     */
    public static void printHeader(String str) {
        System.out.println("\n\n------------------------------------");
        System.out.println(str);
        System.out.println("------------------------------------");
    }


}
